package core.generator;

import java.util.HashMap;
import java.util.Map;

import core.ConstantesAnaliseSemantica;
import core.modeling.metamodel.Classe;
import core.modeling.metamodel.Colecao;
import core.modeling.metamodel.Metodo;
import core.modeling.metamodel.Parametro;
import core.modeling.metamodel.TipoPrimitivoEnum;
import core.nodos.NoParametros;

public class GeradorDeCodigoUtils {
	private static final String NOME_FILTRO = "filtro";
	private static Map<String, Integer> idsFiltros = new HashMap<String, Integer>();

	public static String oclAPreToJML(String contextoDaNavegacao) {
		contextoDaNavegacao = contextoDaNavegacao.replaceAll(ConstantesAnaliseSemantica.APRE, "");
		return "\\old(" + contextoDaNavegacao + ")";
	}

	public static String oclIsNewToJML(String contextoDaNavegacao) {
		contextoDaNavegacao = contextoDaNavegacao.replaceAll("."
				+ ConstantesAnaliseSemantica.OP_OCL_IS_NEW + "\\(\\)", "");
		return "\\fresh(" + contextoDaNavegacao + ")";
	}

	public static String oclIsUndefinedToJML(String contextoDaNavegacao) {
		contextoDaNavegacao = contextoDaNavegacao.replaceAll("."
				+ ConstantesAnaliseSemantica.OP_OCL_IS_UNDEFINED + "\\(\\)", "");
		return "(" + contextoDaNavegacao + " == null)";
	}

	public static String oclLineToJML(String valor) {
		return ConstantesGeracaoDeCodigo.JML_ESPEC_INITIALIZER
				+ ConstantesGeracaoDeCodigo.JML_LINE_INITIALIZER + " " + valor
				+ " " + ConstantesGeracaoDeCodigo.JML_LINE_INITIALIZER
				+ ConstantesGeracaoDeCodigo.JML_ESPEC_FINISHER;
	}

	public static String oclSymbolToJML(String valor) {
		if (valor.equals(ConstantesAnaliseSemantica.SELF)) {
			return "this";
		} else if (valor.equals(ConstantesAnaliseSemantica.RESULT)) {
			return "\\result";
		} else if (valor.equals(ConstantesAnaliseSemantica.PRE)) {
			return "requires";
		} else if (valor.equals(ConstantesAnaliseSemantica.POST)) {
			return "ensures";
		}
		return valor;
	}

	public static String oclOperatorToJML(String valor) {
		if (valor.equals(ConstantesAnaliseSemantica.OP_DIFF)) {
			return "!=";
		} else if (valor.equals(ConstantesAnaliseSemantica.OP_EQUAL)) {
			return "==";
		} else if (valor.equals(ConstantesAnaliseSemantica.OP_UNARY_NOT)) {
			return "!";
		} else if (valor.equals(ConstantesAnaliseSemantica.OP_LOGICAL_AND)) {
			return "&&";
		} else if (valor.equals(ConstantesAnaliseSemantica.OP_LOGICAL_OR)) {
			return "||";
		} else if (valor.equals(ConstantesAnaliseSemantica.OP_LOGICAL_XOR)) {
			return "^";
		} else if (valor.equals(ConstantesAnaliseSemantica.OP_LOGICAL_IMPLIES)) {
			return "==>";
		}
		return valor;
	}

	public static String oclTypeToJava(Classe oclType) {
		String tipoOcl = oclType.getNome();
		String tipoJava = tipoOcl;
		if (oclType.isColecao()) {
			Colecao col = (Colecao) oclType;
			tipoJava = "JMLSet<" + col.getTipoObjetos().getNome() + ">";
		} else if (tipoOcl.equals(TipoPrimitivoEnum.REAL.getNome()))
			tipoJava = "Double";
		else if (tipoOcl.equals(TipoPrimitivoEnum.INTEGER.getNome()))
			tipoJava = "Integer";
		else if (tipoOcl.equals(TipoPrimitivoEnum.BOOLEAN.getNome()))
			tipoJava = "Boolean";
		return tipoJava;
	}

	public static String defaultReturnType(Classe oclType) {
		String tipoOcl = oclType.getNome();
		if (!tipoOcl.equals(ConstantesAnaliseSemantica.VOID)) {
			if (tipoOcl.equals(TipoPrimitivoEnum.REAL.getNome()))
				return "return 0.0;";
			else if (tipoOcl.equals(TipoPrimitivoEnum.INTEGER.getNome()))
				return "return 0;";
			else if (tipoOcl.equals(TipoPrimitivoEnum.STRING.getNome()))
				return "return \\\"\\\";";
			else if (tipoOcl.equals(TipoPrimitivoEnum.BOOLEAN.getNome()))
				return "return false;";
			else
				return "return null;";
		} else {
			return "";
		}
	}

	public static String nextFilterCode(String classe) {
		return NOME_FILTRO + getNextId(classe);
	}

	public static Codigo generateFilterCall(String nomeFiltro, Metodo metodo) {
		String retorno = nomeFiltro + "(";
		String separador = "";
		for (Parametro p : metodo.getParametros()) {
			retorno += separador + p.getNome();
			separador = ", ";
		}
		retorno += ")";
		return new Codigo(retorno);
	}

	public static Codigo generateFilterCode(String nomeFiltro,
			String tipoObjetos, NoParametros params, Metodo metodo) {
		Codigo codigoFiltro = new Codigo();
		codigoFiltro.add(1, "/*@ ");
		codigoFiltro.addLine("private model JMLFilter<" + tipoObjetos + "> "
				+ nomeFiltro + generateParamsFilterCode(metodo) + " {");
		codigoFiltro.add(1, "  @ ");
		codigoFiltro
				.addLine(1, "return new JMLFilter<" + tipoObjetos + ">() {");
		codigoFiltro.add(1, "  @ ");
		codigoFiltro.addLine(2, "public boolean filtra(" + tipoObjetos + " "
				+ ConstantesAnaliseSemantica.DEFAULT_FILTER_PARAMETER_CALL
				+ ") {");
		codigoFiltro.add(1, "  @ ");
		codigoFiltro.addLine(3, "return " + params.getValor() + ";");
		codigoFiltro.add(1, "  @ ");
		codigoFiltro.addLine(2, "}");
		codigoFiltro.add(1, "  @ ");
		codigoFiltro.addLine(1, "};");
		codigoFiltro.add(1, "  @ ");
		codigoFiltro.addLine("} */");
		return codigoFiltro;
	}

	private static String generateParamsFilterCode(Metodo metodo) {
		String retorno = "(";
		String separador = "";
		for (Parametro p : metodo.getParametros()) {
			retorno += separador + "final " + oclTypeToJava(p.getTipo()) + " "
					+ p.getNome();
			separador = ", ";
		}
		return retorno + ")";
	}

	private static int getNextId(String nomeClasse) {
		Integer id = getId(nomeClasse);
		idsFiltros.put(nomeClasse, id + 1);
		return id;
	}
	
	private static int getId(String nomeClasse) {
		Integer id = idsFiltros.get(nomeClasse);
		if (id == null)
			id = 0;
		return id;
	}
}
